package com.singfan.app.controller;

import com.pingplusplus.model.Event;
import com.pingplusplus.model.Webhooks;
import com.singfan.app.controller.model.request.*;
import com.singfan.app.controller.model.response.*;
import com.singfan.app.controller.model.response.partial.OrderCommentPartial;
import com.singfan.app.controller.model.response.partial.OrderCustomerSummaryPartial;
import com.singfan.app.util.AppointmentUtil;
import com.singfan.app.util.RequestParamChecker;
import com.singfan.common.util.CheckUtil;
import com.singfan.common.util.StringUtil;
import com.singfan.core.component.CustomerTokenHolder;
import com.singfan.core.dao.database.model.*;
import com.singfan.core.dao.database.model.constant.HairServiceType;
import com.singfan.core.dao.database.model.constant.OrderStatusType;
import com.singfan.core.dao.database.model.constant.PayChannelType;
import com.singfan.core.service.IOrderService;
import com.singfan.core.service.IShopService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * Author: hansonghe
 * Date: 2015-08-27 10:13
 * Desc:
 */
@Controller
public class OrderController extends SingFanControllerBase {
    private static final String CHARGE_SUCCEEDED = "charge.succeeded";
    private static final String REFUND_SUCCEEDED = "charge.refund";

    @Autowired
    private IOrderService orderService;
    @Autowired
    private IShopService shopService;

    @RequestMapping(value="/v1/order/create", method= RequestMethod.POST)
    public void create(HttpServletRequest request, HttpServletResponse response){
        try{
            OrderCreateResponse result = new OrderCreateResponse();
            OrderCreateRequest orderCreateRequest = request2Bean(request, OrderCreateRequest.class);
            String clientIp = getIpAddress(request);

            String accessToken = orderCreateRequest.getAccessToken();
            int orderType = orderCreateRequest.getOrderType();
            String orderContent = orderCreateRequest.getOrderContent();
            long barberId = orderCreateRequest.getBarberID();
            int appointmentDate = orderCreateRequest.getAppointmentDate();
            int appointmentTime = orderCreateRequest.getAppointmentTime();
            int payChannelType =orderCreateRequest.getPayChannelType();
            int price = orderCreateRequest.getPrice();
            long couponId = orderCreateRequest.getCouponId();

            long customerId = 0;
            CustomerToken customerToken = CustomerTokenHolder.getCurrentCustomerToken();
            if (customerToken != null){
                customerId = customerToken.getCustomerId();
            }

            BarberHairstyle barberHairstyle = null;
            List<Integer> hairServiceIdList = null;
            int realOriginalPriceCount = 0;
            if (customerId > 0){
                boolean checkResult = true;
                if (!checkAppointment(appointmentDate, appointmentTime)){
                    checkResult = false;
                }
                else if (!PayChannelType.isPayChannelType(payChannelType)){
                    checkResult = false;
                }
                else if (!(orderType == 1 || orderType == 2)){
                    checkResult = false;
                }
                else if(StringUtil.isNullOrEmpty(orderContent)){
                    checkResult = false;
                }
                else if(price<=0){
                    checkResult = false;
                }
                else {
                    if (orderType == 1 && CheckUtil.isPositiveInteger(orderContent)){
                        long hairStyleId = Long.parseLong(orderContent);
                        barberHairstyle =  barberService.findBarberHairstyle(hairStyleId);
                        if (barberHairstyle == null){
                            checkResult = false;
                        }
                        else{
                            realOriginalPriceCount = barberHairstyle.getPrice();
                        }
                    }
                    else {
                        List<String> hairServiceIdStrList = StringUtil.split(orderContent, ";");
                        hairServiceIdList = new ArrayList<>(hairServiceIdStrList.size());
                        for (String hairServiceIdStr : hairServiceIdStrList){
                            if (CheckUtil.isPositiveInteger(hairServiceIdStr)){
                                int hairServiceId = Integer.parseInt(hairServiceIdStr);
                                hairServiceIdList.add(hairServiceId);
                            }
                            else {
                                checkResult = false;
                                break;
                            }
                        }
                    }
                }

                if (checkResult){
                    Customer customer = customerService.findCustomerByToken(orderCreateRequest.getAccessToken());
                    Barber barber = barberService.findBarber(barberId);
                    Shop shop = shopService.findShop(barber.getShopId());
                    List<ShopHairService> shopHairServiceList = null;

                    if (orderType == 2){
                        shopHairServiceList = shopService.selectShopHairServiceList(barber.getShopId(), hairServiceIdList);
                        for (ShopHairService shopHairService :shopHairServiceList){
                            if (shopHairService.getServiceType() == HairServiceType.HAIR_CUT){
                                realOriginalPriceCount += barber.getHairCutRealPrice();
                            }
                            else if (shopHairService.getServiceType() == HairServiceType.HAIR_MODELING){
                                realOriginalPriceCount += barber.getHairModelingRealPrice();
                            }
                            else {
                                realOriginalPriceCount += shopHairService.getRealPrice();
                            }
                        }
                    }

                    int realPriceCount;
                    if (couponId > 0){
                        CustomerCoupon customerCoupon = customerService.findCustomerCoupon(couponId);
                        if (customerCoupon != null){
                            if ((customerCoupon.getCustomerId() == customerId)
                                    && (customerCoupon.getShopId() == 0 || customerCoupon.getShopId() == shop.getId())
                                    && (realOriginalPriceCount > customerCoupon.getPriceCondition())
                                    && (customerCoupon.getExpiredTime().getTime() > System.currentTimeMillis())
                                    && customerCoupon.getOrderId() == 0){
                                realPriceCount = realOriginalPriceCount - customerCoupon.getAmount();
                                if (realPriceCount <= 0){
                                    realPriceCount = 100;
                                }
                            }
                            else{
                                realPriceCount = Integer.MAX_VALUE;
                            }
                        }
                        else {
                            realPriceCount = Integer.MAX_VALUE;
                        }
                    }
                    else{
                        realPriceCount = realOriginalPriceCount;
                    }

                    if (realPriceCount == price){
                        String charge = orderService.createOrder(customer, shop, barber, orderType, barberHairstyle, shopHairServiceList, payChannelType, couponId, realOriginalPriceCount, realPriceCount, appointmentDate, appointmentTime, clientIp);
                        if (IOrderService.APPOINTMENT_CONFLICT.equals(charge)){
                            result.setResultCode(ResultCode.USER_APPOINTMENT_CONFLICT);
                            result.setCharge(null);

                            traceLog.error(String.format("OrderController.create() error with USER_APPOINTMENT_CONFLICT -> resultCode:%1$d, ip:%2$s, accessToken:%3$s, barberId:%4$d, appointmentDate:%5$s, appointmentTime:%6$d, payChannelType:%7$d, price:%8$d, orderType:%9$d, orderContent:%10$s, couponId:%11$d", ResultCode.USER_APPOINTMENT_CONFLICT, clientIp, accessToken, barberId, appointmentDate, appointmentTime, payChannelType, price, orderType, orderContent, couponId));
                        }
                        else {
                            result.setResultCode(ResultCode.SUCCESS);
                            result.setCharge(charge);

                            traceLog.debug(String.format("OrderController.create() successful -> resultCode:%1$d, ip:%2$s, accessToken:%3$s, barberId:%4$d, appointmentDate:%5$s, appointmentTime:%6$d, payChannelType:%7$d, price:%8$d, orderType:%9$d, orderContent:%10$s, couponId:%11$d", ResultCode.SUCCESS, clientIp, accessToken, barberId, appointmentDate, appointmentTime, payChannelType, price, orderType, orderContent, couponId));
                        }
                    }
                    else {
                        result.setResultCode(ResultCode.REQUEST_PARAM_ERROR);

                        traceLog.error(String.format("OrderController.create() error with REQUEST_PARAM_ERROR -> resultCode:%1$d, ip:%2$s, accessToken:%3$s, barberId:%4$d, appointmentDate:%5$s, appointmentTime:%6$d, payChannelType:%7$d, price:%8$d, orderType:%9$d, orderContent:%10$s, couponId:%11$d", ResultCode.REQUEST_PARAM_ERROR, clientIp, accessToken, barberId, appointmentDate, appointmentTime, payChannelType, price, orderType, orderContent, couponId));
                    }
                }
                else {
                    result.setResultCode(ResultCode.REQUEST_PARAM_ERROR);

                    traceLog.error(String.format("OrderController.create() error with REQUEST_PARAM_ERROR -> resultCode:%1$d, ip:%2$s, accessToken:%3$s, barberId:%4$d, appointmentDate:%5$s, appointmentTime:%6$d, payChannelType:%7$d, price:%8$d, orderType:%9$d, orderContent:%10$s, couponId:%11$d", ResultCode.REQUEST_PARAM_ERROR, clientIp, accessToken, barberId, appointmentDate, appointmentTime, payChannelType, price, orderType, orderContent, couponId));
                }
            }
            else {
                result.setResultCode(ResultCode.ACCESS_TOKEN_ERROR);

                traceLog.error(String.format("OrderController.create() error with ACCESS_TOKEN_ERROR -> resultCode:%1$d, ip:%2$s, accessToken:%3$s, barberId:%4$d, appointmentDate:%5$s, appointmentTime:%6$d, payChannelType:%7$d, price:%8$d, orderType:%9$d, orderContent:%10$s, couponId:%11$d", ResultCode.ACCESS_TOKEN_ERROR, clientIp, accessToken, barberId, appointmentDate, appointmentTime, payChannelType, price, orderType, orderContent, couponId));
            }

            bean2Response(response, result);
        }
        catch (Exception ex){
            System.out.println(ex.getMessage());
            ex.printStackTrace();
        }
    }

    @RequestMapping(value="/v1/order/ping_call_back", method= RequestMethod.POST)
    public void pingCallBack(HttpServletRequest request, HttpServletResponse response){
        try {
            request.setCharacterEncoding("UTF8");
            // 获得 http body 内容
            BufferedReader reader = request.getReader();
            StringBuilder buffer = new StringBuilder();
            String string;
            while ((string = reader.readLine()) != null) {
                buffer.append(string);
            }
            reader.close();
            // 解析异步通知数据
            Event event = Webhooks.eventParse(buffer.toString());
            String eventType = event.getType();
            if (CHARGE_SUCCEEDED.equals(eventType)) {
                //支付成功
                response.setStatus(200);
                Map<String, Object> objectMap = (Map<String, Object>)event.getData().get("object");
                String chargeId = objectMap.get("id").toString();
                orderService.updateOrderPaied(chargeId);

                traceLog.debug(String.format("OrderController.pingCallBack() charge success -> payChannelSerialNumber:%1$s", chargeId));
            }
            else if(REFUND_SUCCEEDED.equals(eventType)){
                //退款成功
                response.setStatus(200);

                Map<String, Object> objectMap = (Map<String, Object>)event.getData().get("object");
                String chargeId = objectMap.get("id").toString();

                traceLog.debug(String.format("OrderController.pingCallBack() refund success -> payChannelSerialNumber:%1$s", chargeId));
            }
            else {
                response.setStatus(500);

                traceLog.error(String.format("OrderController.pingCallBack() failed with unknown event -> eventType:%1$s payChannelSerialNumber:%2$s", eventType, event.getId()));
            }
        }
        catch (Exception ex){
            response.setStatus(500);

            traceLog.error(String.format("OrderController.pingCallBack() failed -> msg:%1$s", ex.getMessage()));
        }
    }

    @RequestMapping(value="/v1/order/customer/list", method= RequestMethod.POST)
    public void customerList(HttpServletRequest request, HttpServletResponse response){
        OrderCustomerListResponse result = new OrderCustomerListResponse();
        OrderCustomerListRequest orderCustomerListRequest = request2Bean(request, OrderCustomerListRequest.class);
        String ip = getIpAddress(request);

        String accessToken = orderCustomerListRequest.getAccessToken();
        int orderType = orderCustomerListRequest.getOrderType();
        int pageIndex = orderCustomerListRequest.getPageIndex();
        int pageSize = orderCustomerListRequest.getPageSize();

        long customerId = 0;
        CustomerToken customerToken = CustomerTokenHolder.getCurrentCustomerToken();
        if (customerToken != null){
            customerId = customerToken.getCustomerId();
        }

        if (customerId > 0){
            boolean checkResult = true;
            if (!(OrderStatusType.isOrderStatusType(orderType) || orderType == 0)){
                checkResult = false;
            }
            else if (!RequestParamChecker.checkPageIndexAndSize(pageIndex, pageSize)){
                checkResult = false;
            }

            if (checkResult){
                List<Orders> orderList = orderService.findCustomerOrderList(customerId, orderType, pageIndex, pageSize);
                List<OrderCustomerSummaryPartial> orderCustomerSummaryPartialList = new ArrayList<>();
                for (Orders order : orderList){
                    OrderCustomerSummaryPartial orderCustomerSummaryPartial = new OrderCustomerSummaryPartial();
                    orderCustomerSummaryPartial.setOrderImgUrl(order.getOrderIcon());
                    orderCustomerSummaryPartial.setOrderName(order.getOrderName());
                    orderCustomerSummaryPartial.setBarberId(order.getBarberId());
                    orderCustomerSummaryPartial.setBarberName(order.getBarberName());
                    orderCustomerSummaryPartial.setShopName(order.getShopName());
                    orderCustomerSummaryPartial.setAddressText(order.getShopAddress());
                    orderCustomerSummaryPartial.setLongitude(String.valueOf(order.getLongitude().doubleValue()));
                    orderCustomerSummaryPartial.setLatitude(String.valueOf(order.getLatitude().doubleValue()));
                    orderCustomerSummaryPartial.setOrderPrice(order.getPrice());
                    orderCustomerSummaryPartial.setOrderStatus(order.getStatus());
                    orderCustomerSummaryPartial.setOrderCommentStar(order.getCommentStar());
                    orderCustomerSummaryPartial.setOrderCreateTime(order.getCreateTime().getTime());
                    orderCustomerSummaryPartial.setOrderAppointmentDay(String.valueOf(order.getAppointmentDay()));
                    orderCustomerSummaryPartial.setOrderAppointmentClock(order.getAppointmentClock());
                    orderCustomerSummaryPartial.setOrderID(order.getId());

                    orderCustomerSummaryPartialList.add(orderCustomerSummaryPartial);
                }

                result.setResultCode(ResultCode.SUCCESS);
                result.setOrderSummaryList(orderCustomerSummaryPartialList);

                traceLog.error(String.format("OrderController.customerList() successful -> resultCode:%1$d, ip:%2$s, accessToken:%3$s, orderType:%4$d, pageIndex:%5$d, pageSize:%6$d", ResultCode.SUCCESS, ip, accessToken, orderType, pageIndex, pageSize));
            }
            else{
                result.setResultCode(ResultCode.REQUEST_PARAM_ERROR);

                traceLog.error(String.format("OrderController.customerList() failed with REQUEST_PARAM_ERROR -> resultCode:%1$d, ip:%2$s, accessToken:%3$s, orderType:%4$d, pageIndex:%5$d, pageSize:%6$d", ResultCode.REQUEST_PARAM_ERROR, ip, accessToken, orderType, pageIndex, pageSize));
            }
        }
        else {
            result.setResultCode(ResultCode.ACCESS_TOKEN_ERROR);

            traceLog.error(String.format("OrderController.customerList() failed with ACCESS_TOKEN_ERROR -> resultCode:%1$d, ip:%2$s, accessToken:%3$s, orderType:%4$d, pageIndex:%5$d, pageSize:%6$d", ResultCode.SUCCESS, ip, accessToken, orderType, pageIndex, pageSize));
        }

        bean2Response(response, result);
    }

    @RequestMapping(value="/v1/order/repay", method= RequestMethod.POST)
    public void repay(HttpServletRequest request, HttpServletResponse response) {
        OrderRepayResponse result = new OrderRepayResponse();
        OrderRepayRequest orderRepayRequest = request2Bean(request, OrderRepayRequest.class);
        String ip = getIpAddress(request);

        String accessToken = orderRepayRequest.getAccessToken();
        long orderId = orderRepayRequest.getOrderId();

        CustomerToken customerToken = CustomerTokenHolder.getCurrentCustomerToken();
        if (customerToken != null) {
            OrderPayChannelInfo orderPayChannelInfo = orderService.findOrderPayChannelInfo(orderId);
            if (orderPayChannelInfo != null){
                result.setCharge(orderPayChannelInfo.getChargeContent());
            }

            result.setResultCode(ResultCode.SUCCESS);
            traceLog.debug(String.format("OrderController.repay() successful -> resultCode:%1$d, ip:%2$s, accessToken:%3$s, orderId:%4$d", ResultCode.SUCCESS, ip, accessToken, orderId));
        }
        else {
            result.setResultCode(ResultCode.ACCESS_TOKEN_ERROR);

            traceLog.error(String.format("OrderController.repay() failed with ACCESS_TOKEN_ERROR -> resultCode:%1$d, ip:%2$s, accessToken:%3$s, orderId:%4$d", ResultCode.ACCESS_TOKEN_ERROR, ip, accessToken, orderId));
        }

        bean2Response(response, result);
    }

    @RequestMapping(value="/v1/order/comment", method= RequestMethod.POST)
    public void comment(HttpServletRequest request, HttpServletResponse response) {
        OrderCommentResponse result = new OrderCommentResponse();
        OrderCommentRequest orderCommentRequest = request2Bean(request, OrderCommentRequest.class);
        String ip = getIpAddress(request);

        String accessToken = orderCommentRequest.getAccessToken();
        long orderId = orderCommentRequest.getOrderId();
        int serviceLevel = orderCommentRequest.getServiceLevel();
        int techLevel = orderCommentRequest.getTechLevel();
        int environmentLevel = orderCommentRequest.getEnvironmentLevel();
        String commentContent = orderCommentRequest.getCommentContent();
        List<String> commentImgUrl = orderCommentRequest.getCommentImgUrl();
        if (commentImgUrl == null){
            commentImgUrl = new ArrayList<>();
        }

        CustomerToken customerToken = CustomerTokenHolder.getCurrentCustomerToken();
        if (customerToken != null) {
            boolean checkResult = true;
            if(!RequestParamChecker.checkStarLevel(serviceLevel)){
                checkResult = false;
            }
            else if(!RequestParamChecker.checkStarLevel(techLevel)){
                checkResult = false;
            }
            else if(!RequestParamChecker.checkStarLevel(environmentLevel)){
                checkResult = false;
            }
            else if(StringUtil.isNullOrEmpty(commentContent)){
                checkResult = false;
            }

            if (checkResult){
                orderService.commentOrder(orderId, customerToken.getCustomerId(), serviceLevel, techLevel, environmentLevel, commentContent, commentImgUrl);

                traceLog.debug(String.format("OrderController.comment() successful -> resultCode:%1$d, ip:%2$s, accessToken:%3$s, orderId:%4$d, serviceLevel:%5$d, techLevel:%6$d, environmentLevel:%7$d, commentContent:%8$s, commentImgUrlCount:%9$d", ResultCode.SUCCESS, ip, accessToken, orderId, serviceLevel, techLevel, environmentLevel, commentContent, commentImgUrl.size()));
            }
            else {
                traceLog.error(String.format("OrderController.comment() failed with REQUEST_PARAM_ERROR -> resultCode:%1$d, ip:%2$s, accessToken:%3$s, orderId:%4$d, serviceLevel:%5$d, techLevel:%6$d, environmentLevel:%7$d, commentContent:%8$s, commentImgUrlCount:%9$d", ResultCode.REQUEST_PARAM_ERROR, ip, accessToken, orderId, serviceLevel, techLevel, environmentLevel, commentContent, commentImgUrl.size()));
            }
        }
        else {
            result.setResultCode(ResultCode.ACCESS_TOKEN_ERROR);

            traceLog.error(String.format("OrderController.comment() failed with ACCESS_TOKEN_ERROR -> resultCode:%1$d, ip:%2$s, accessToken:%3$s, orderId:%4$d, serviceLevel:%5$d, techLevel:%6$d, environmentLevel:%7$d, commentContent:%8$s, commentImgUrlCount:%9$d", ResultCode.ACCESS_TOKEN_ERROR, ip, accessToken, orderId, serviceLevel, techLevel, environmentLevel, commentContent, commentImgUrl.size()));
        }

        bean2Response(response, result);
    }

    @RequestMapping(value="/v1/order/comment/list", method= RequestMethod.POST)
    public void commentList(HttpServletRequest request, HttpServletResponse response) {
        OrderCommentListResponse result = new OrderCommentListResponse();
        OrderCommentListRequest orderCommentListRequest = request2Bean(request, OrderCommentListRequest.class);
        String ip = getIpAddress(request);

        String accessToken = orderCommentListRequest.getAccessToken();
        long barberId = orderCommentListRequest.getBarberId();
        int pageIndex = orderCommentListRequest.getPageIndex();
        int pageSize = orderCommentListRequest.getPageSize();

        if (RequestParamChecker.checkPageIndexAndSize(pageIndex, pageSize)){
            List<BarberComment> barberCommentList = barberService.findBarberCommentList(barberId, pageIndex, pageSize);
            List<OrderCommentPartial> orderCommentPartialList = new ArrayList<>(barberCommentList.size());
            for (BarberComment barberComment : barberCommentList){
                OrderCommentPartial orderCommentPartial = new OrderCommentPartial();
                orderCommentPartial.setCustomName(barberComment.getCustomerName());
                orderCommentPartial.setCustomAvatar(barberComment.getCustomerAvatar());
                orderCommentPartial.setCommentContent(barberComment.getCommentText());
                orderCommentPartial.setCommentImgUrlList(StringUtil.split(barberComment.getCommentImgList(), ";"));
                orderCommentPartial.setCommentLevel(barberComment.getCommentStar());
                orderCommentPartial.setCommentTime(barberComment.getCreateTime().getTime());

                orderCommentPartialList.add(orderCommentPartial);
            }

            result.setResultCode(ResultCode.SUCCESS);
            result.setOrderCommentPartialList(orderCommentPartialList);
            traceLog.debug(String.format("OrderController.commentList() successful -> resultCode:%1$d, ip:%2$s, accessToken:%3$s, barberId:%4$d, pageIndex:%5$d, pageSize:%6$d", ResultCode.SUCCESS, ip, accessToken, barberId, pageIndex, pageSize));
        }
        else {
            result.setResultCode(ResultCode.REQUEST_PARAM_ERROR);
            traceLog.error(String.format("OrderController.commentList() failed with REQUEST_PARAM_ERROR -> resultCode:%1$d, ip:%2$s, accessToken:%3$s, barberId:%4$d, pageIndex:%5$d, pageSize:%6$d", ResultCode.REQUEST_PARAM_ERROR, ip, accessToken, barberId, pageIndex, pageSize));
        }

        bean2Response(response, result);
    }

    private boolean checkAppointment(int appointmentDate, int appointmentTime) {
        boolean checkResult = true;
        int todayTime = AppointmentUtil.getTodayTime();
        if (!(appointmentDate > -1 && RequestParamChecker.checkAppointmentDayTime(appointmentDate))){
            checkResult = false;
        }
        else if(!(appointmentTime > -1 || (todayTime == appointmentDate && appointmentTime > AppointmentUtil.getNowClock()))){
            checkResult = false;
        }

        return checkResult;
    }
}
